﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

using LanguageDetector;
using LanguageDetector.Data;

namespace LanguageDetector.Test
{
    [TestFixture]
    public class Logic01_Trainer : IDisposable
    {
        #region vars + setup/teardown
        LanguageStat _lang;
        TrainerLogic _trainer = null;

        //setup
        public Logic01_Trainer()
        {
            _lang = new LanguageStat()
            {
                ISO2 = "30256",
                Name = "t020467787",
            };
            var errors = LanguageStatDB.Save(new List<LanguageStat> { _lang });
            Assert.IsEmpty(errors);
            Assert.Greater(_lang.ID, 0);
        }

        //teardown
        bool _disposed = false;
        public void Dispose()
        {
            lock (this)
            {
                if (!_disposed)
                {
                    _disposed = true;
                    var errors = new List<Exception>();
                    if (_lang != null && _lang.ID > 0)
                        errors.AddRange(LanguageStatDB.Delete(new List<int> { _lang.ID }));
                    if (_trainer != null && !_trainer.Result.IsNullOrEmpty())
                    {
                        var ids = (from bg in _trainer.Result.Values where bg.ID > 0 select bg.ID).ToList();
                        if (!ids.IsNullOrEmpty())
                        {
                            var sb = new StringBuilder("delete from bigram_stats where id in (");
                            ids.ForEach(id => sb.AppendFormat("{0},", id));
                            sb.Length--;
                            sb.Append(");");
                            var q = new Query() { Text = sb.ToString() };
                            errors.AddRange(q.ExecuteNoneReader());
                        }
                    }
                    Assert.IsEmpty(errors);
                }
            }
        }
        ~Logic01_Trainer()
        {
            Dispose();
        }
        #endregion

        bool _init = false;
        [Test]
        public void T10_TRAINING_INIT()
        {
            _trainer = new TrainerLogic(_lang, o => o.ToString()) { AutoSaveToDB = true };
            Assert.IsNotNull(_trainer);
            Assert.IsNotNull(_trainer.LanguageStat);
            Assert.AreEqual(_lang.ID, _trainer.LanguageStat.ID);
            Assert.IsNotNull(_trainer.Result);
            Assert.IsEmpty(_trainer.Errors);

            _trainer.Done += new Action<TrainerLogic, TrainerDoneEventArgs>(_trainer_Done);
            _init = true;
        }

        bool _run1 = false;
        [Test]
        public void T20_TRANING_RUN1_1WORD()
        {
            if (!_init)
                T10_TRAINING_INIT();

            _doneCount = 0;
            _trainer.Run("is");
            TrainerTest(_trainer, 1, 1);
            BiGramTest(_trainer.Result[new BiGram('i','s')], 1);
            Assert.AreEqual(1, _doneCount);

            _run1 = true;
        }

        int _doneCount = 0;
        void _trainer_Done(TrainerLogic caller, TrainerDoneEventArgs args)
        {
            Assert.IsNotNull(caller);
            Assert.IsNotNull(args);
            Assert.IsNotNull(args.LanguageStat);
            Assert.AreEqual(_lang.ID, args.LanguageStat.ID);
            Assert.IsNotNull(args.Result);
            Assert.Greater(args.Result.Count, 0);
            Assert.IsEmpty(args.Errors);

            _doneCount++;
        }

        void TrainerTest(TrainerLogic trainer, int expectedCount, int expectedSum)
        {
            Assert.IsNotEmpty(trainer.Result);
            Assert.AreEqual(expectedCount, trainer.Result.Count);
            Assert.Greater(trainer.LanguageStat.BiGramTotal, 0);
            Assert.AreEqual(expectedSum, trainer.LanguageStat.BiGramTotal);
        }

        void BiGramTest(BiGramStat bg, int expectedCount)
        {
            Assert.IsNotNull(bg);
            Assert.Greater(bg.ID, 0);
            Assert.AreEqual(expectedCount, bg.Count);
            Assert.AreEqual(_lang.ID, bg.LanguageID);
        }

        bool _run2 = false;
        [Test]
        public void T21_TRANING_RUN2_2WORDS()
        {
            if (!_run1)
                T20_TRANING_RUN1_1WORD();

            _doneCount = 0;
            _trainer.Run("IS at");
            TrainerTest(_trainer, 2, 3);
            BiGramTest(_trainer.Result[new BiGram('i','s')], 2);
            BiGramTest(_trainer.Result[new BiGram('a', 't')], 1);
            Assert.AreEqual(1, _doneCount);

            _run2 = true;
        }

        [Test]
        public void T22_TRAINING_RUN3_4WORDS()
        {
            if (!_run2)
                T21_TRANING_RUN2_2WORDS();

            var vals = new List<ExpectedBC> 
            {
                new ExpectedBC(new BiGram('i', 't'), 1),
                new ExpectedBC(new BiGram('i', 's'), 3),
                new ExpectedBC(new BiGram('a', 't'), 2),
                new ExpectedBC(new BiGram('h', 'o'), 1),
                new ExpectedBC(new BiGram('o', 'm'), 1),
                new ExpectedBC(new BiGram('m', 'e'), 1),
            };

            _doneCount = 0;
            _trainer.Run("It is at Home!");
            var sum = (from x in vals select x.Count).Sum();
            TrainerTest(_trainer, vals.Count, sum);
            vals.ForEach(x => BiGramTest(_trainer.Result[x.BiGram], x.Count));
            Assert.AreEqual(1, _doneCount);
        }

        class ExpectedBC
        {
            public ExpectedBC(BiGram bigram, int count)
            {
                BiGram = bigram;
                Count = count;
            }
            public BiGram BiGram;
            public int Count;
        }
    }
}
